Repository navigation
Teach stubsabot to be smarter about the required locations of py.typed files - #11053
Conversation
|
Would this approach work?
|
Yes, that does sound like a promising alternative. I'll give it a try and see how it compares. |
|
That made it a lot simpler, thanks! |
|
Have you backtested this on a set of known py.typed packages? It's possible things like shipping an untyped tests/ are not uncommon. (to be clear, I'm fine with some false negatives, but curious if we know what they would look like in practice) |
|
I'm probably not going to review this. I'm not super familiar with the internal layout of various Python packages or stubsabot. |
I haven't done exhaustive testing (mostly because I became exhausted). There are definitely distributions in typeshed that have some "interesting" things shipped in their sdists: take a look at Details>>>
with tarfile.open(r"C:\Users\alexw\Downloads\docopt-0.6.2.tar.gz", mode="r:gz") as tf:
... tf.list(verbose=False)
...
docopt-0.6.2/
docopt-0.6.2/docopt.egg-info/
docopt-0.6.2/docopt.egg-info/dependency_links.txt
docopt-0.6.2/docopt.egg-info/PKG-INFO
docopt-0.6.2/docopt.egg-info/SOURCES.txt
docopt-0.6.2/docopt.egg-info/top_level.txt
docopt-0.6.2/docopt.py
docopt-0.6.2/examples/
docopt-0.6.2/examples/arguments_example.py
docopt-0.6.2/examples/calculator_example.py
docopt-0.6.2/examples/counted_example.py
docopt-0.6.2/examples/git/
docopt-0.6.2/examples/git/git.py
docopt-0.6.2/examples/git/git_add.py
docopt-0.6.2/examples/git/git_branch.py
docopt-0.6.2/examples/git/git_checkout.py
docopt-0.6.2/examples/git/git_clone.py
docopt-0.6.2/examples/git/git_commit.py
docopt-0.6.2/examples/git/git_push.py
docopt-0.6.2/examples/git/git_remote.py
docopt-0.6.2/examples/naval_fate.py
docopt-0.6.2/examples/odd_even_example.py
docopt-0.6.2/examples/options_example.py
docopt-0.6.2/examples/options_shortcut_example.py
docopt-0.6.2/examples/quick_example.py
docopt-0.6.2/examples/validation_example.py
docopt-0.6.2/LICENSE-MIT
docopt-0.6.2/MANIFEST.in
docopt-0.6.2/PKG-INFO
docopt-0.6.2/README.rst
docopt-0.6.2/setup.cfg
docopt-0.6.2/setup.pyPresumably if |
very reasonable! |
|
I'll try to do more manual testing of this tomorrow. |
|
I made this diff locally, committed it, and then ran DiffThis was the output (all looks good as far as I can see): Details |
|
I wrote this script (and saved it inside the Scriptimport asyncio
import aiohttp
from stubsabot import fetch_pypi_info, release_contains_py_typed
py_typed_packages = [
# "mypy",
"Flask-SQLAlchemy",
"SQLAlchemy",
"typeshed-stats",
"urllib3",
"annoy",
"freezegun",
"certifi",
"cryptography",
"selenium",
"emoji",
"dj-database-url",
# "pyvmomi",
"invoke",
"babel",
"chardet",
"prettytable",
"termcolor",
"xxhash",
"orjson",
"attrs",
]
async def check_package_detected_as_py_typed(distribution: str, session: aiohttp.ClientSession) -> None:
pypi_info = await fetch_pypi_info(distribution, session=session)
latest_release = next(release for release in pypi_info.releases_in_descending_order() if not release.version.is_prerelease)
assert await release_contains_py_typed(latest_release, session=session), distribution
async def check_all_packages() -> None:
async with aiohttp.ClientSession() as session:
tasks = (check_package_detected_as_py_typed(package, session) for package in py_typed_packages)
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(check_all_packages())The mypy assertion fails because the The pyvmomi assertion fails because the |
|
So, answering @hauntsaninja's question from #11053 (comment) -- overall, I think this improves the balance between false positives and false negatives. It gets rid of the clear false positive from #11049, and I've only found one possible false negative ( |
hauntsaninja
left a comment
There was a problem hiding this comment.
Thanks for looking into it!
An attempt to fix the bug that resulted in #11049.
The approach is this:If there are no directories at the top level of a wheel or sdist (except for the.dist-infodirectory), none of the packages for this distribution can bepy.typedElse, return True if all top-level directories (except for.dist-info) are marked aspy.typed:for each directory, if it contains at least one file, consider it marked aspy.typedif it has apy.typedfile at the top-levelElse, if it only contains subdirectories, consider it marked aspy.typedif all subdirectories are marked aspy.typed(applying the same logic recursively).Edit: all the above is out of date. The PR now uses the better approach suggested by Akuli in #11053 (comment).
I've tested this a fair bit locally, and I think it does the right thing, but would appreciate careful eyes on this and additional testing